package org.nf.web.servlet.param;

import cn.hutool.core.util.ReflectUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.nf.web.servlet.annotation.Model;
import org.nf.web.servlet.ServletApi;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;

/**
 * @author wangl
 * @date 2022/6/13
 * 负责解析带有Model注解的参数
 */
public class ModelMethodArgumentResolver extends AbstractMethodArgumentResolver {

    @Override
    public boolean supports(Parameter parameter) {
        return parameter.isAnnotationPresent(Model.class);
    }

    @Override
    public Object resolve(Parameter parameter, ServletApi servletApi) {
        try {
            Class<?> parameterType = parameter.getType();
            Object instance = ReflectUtil.newInstance(parameterType);
            BeanInfo beanInfo = Introspector.getBeanInfo(parameterType, Object.class);
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                Object value = convert(pd, servletApi.request());
                if (value != null) {
                    ReflectUtil.invoke(instance, pd.getWriteMethod(), value);
                }
            }
            return instance;
        } catch (Exception e) {
            throw new RuntimeException("Resolve argument error.", e);
        }
    }

    /**
     * 将请求参数转换为对象字段相应的类型
     * @param pd 属性描述起
     * @param request 请求对象
     * @return 转换后的值
     */
    private Object convert(PropertyDescriptor pd, HttpServletRequest request) {
        Object value = null;
        String paramName = pd.getName();
        Class<?> paramType = pd.getPropertyType();
        if(paramType.isArray()) {
            paramType = paramType.getComponentType();
            value = toArray(paramType, request.getParameterValues(paramName));
        } else if(Collection.class.isAssignableFrom(paramType)) {
            //获取泛型参数类型
            ParameterizedType parameterizedType = (ParameterizedType) pd.getReadMethod().getGenericReturnType();
            Class<?> genericType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
            value = toCollection(paramType, genericType, request.getParameterValues(paramName));
        } else {
            value = toValue(paramType, request.getParameter(paramName));
        }
        return value;
    }
}
